package com.yyplatform.admin.platform.schedule.service.impl;

import java.util.List;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.modelmapper.ModelMapper;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.yyplatform.admin.platform.schedule.model.dto.JobDTO;
import com.yyplatform.admin.platform.schedule.model.dto.JobFormDTO;
import com.yyplatform.admin.platform.schedule.model.dto.JobQueryDTO;
import com.yyplatform.admin.platform.schedule.model.entity.JobEntity;
import com.yyplatform.admin.platform.schedule.model.enums.JobActionEnum;
import com.yyplatform.admin.platform.schedule.model.JobEntityVO;
import com.yyplatform.admin.platform.schedule.quartz.QuartzProxyJob;
import com.yyplatform.admin.platform.schedule.repository.JobRepository;
import com.yyplatform.admin.platform.schedule.service.JobService;
import com.yyplatform.admin.infra.model.enums.StatusEnum;
import com.yyplatform.admin.infra.exception.ErrorCode;
import com.yyplatform.admin.infra.exception.ServiceException;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author sleepforests@163.com
 * <p>
 * Created on 2024/9/21 22:50
 */
@Service
@Slf4j
@AllArgsConstructor
public class JobServiceImpl implements JobService {


    /**
     * 任务调度参数key
     */


    private Scheduler scheduler;
    private JobRepository jobRepository;
    private ModelMapper modelMapper;


    @Override
    public Page<JobDTO> query(JobQueryDTO pageQueryDTO) {
        Page<JobEntity> jobEntityPage =
                jobRepository.find(pageQueryDTO.getBeanName(), pageQueryDTO.getStatus(), pageQueryDTO.getPageRequest());

        //        List<JobExecutionContext> jobList = null;
        //        try {
        //            jobList = scheduler.getCurrentlyExecutingJobs();
        //        } catch (SchedulerException e) {
        //            log.error("get getCurrentlyExecutingJobs error", e);
        //            jobList = new ArrayList<>();
        //        }
        //
        //        log.info(" running job size is {}", jobList.size());
        //
        //        Map<String, JobExecutionContext> jobMap = new HashMap<>();
        //        for (JobExecutionContext jobExecutionContext : jobList) {
        //            jobMap.put(jobExecutionContext.getJobDetail().getKey().toString(), jobExecutionContext);
        //        }


        List<JobDTO> jobDTOList = jobEntityPage.toList().stream().map(e -> {
            JobDTO jobDTO = modelMapper.map(e, JobDTO.class);

            try {
                boolean check = scheduler.checkExists(getJobKey(e.getId()));
                if (check) {
                    Trigger trigger = scheduler.getTrigger(getTriggerKey(e.getId()));
                    if (trigger != null) {
                        jobDTO.setNextFireTime(trigger.getNextFireTime());
                    }
                }
            } catch (SchedulerException ex) {
                log.error("get getCurrentlyExecutingJobs error", e);
            }


            return jobDTO;
        }).toList();


        return new PageImpl<>(jobDTOList, pageQueryDTO.getPageRequest(), jobEntityPage.getTotalElements());
    }

    @Override
    public JobDTO get(Long id) {
        JobEntity jobEntity = jobRepository.getReferenceById(id);
        return modelMapper.map(jobEntity, JobDTO.class);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(JobFormDTO formDTO) {
        JobEntity jobEntity = modelMapper.map(formDTO, JobEntity.class);
        jobEntity.setStatus(StatusEnum.NORMAL.getValue());

        jobRepository.save(jobEntity);
        createScheduleJob(jobEntity);

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(JobFormDTO formDTO) {
        JobEntity jobEntity = modelMapper.map(formDTO, JobEntity.class);


        jobRepository.save(jobEntity);
        updateScheduleJob(jobEntity);


    }

    @Override
    public void delete(Long id) {
        JobEntity jobEntity = jobRepository.getReferenceById(id);

        deleteScheduleJob(jobEntity);
        jobRepository.deleteById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doAction(Long id, String action) {
        JobEntity jobEntity = jobRepository.getReferenceById(id);


        if (StringUtils.equals(action, JobActionEnum.RUN.getValue())) {
            run(jobEntity);


            jobEntity.setStatus(StatusEnum.NORMAL.getValue());
            jobRepository.save(jobEntity);
            return;
        }


        if (StringUtils.equals(action, JobActionEnum.PAUSE.getValue())) {
            pauseJob(jobEntity);
            jobEntity.setStatus(StatusEnum.PAUSE.getValue());
            jobRepository.save(jobEntity);
            return;
        }

        if (StringUtils.equals(action, JobActionEnum.RESUME.getValue())) {
            resumeJob(jobEntity);

            jobEntity.setStatus(StatusEnum.NORMAL.getValue());
            jobRepository.save(jobEntity);
            return;
        }

    }

    @Override
    public void initJob() {
        List<JobEntity> jobEntityList = jobRepository.findAll().stream().toList();

        for (JobEntity jobEntity : jobEntityList) {
            createScheduleJob(jobEntity);
        }
    }


    // ==========================================================================================================================================================================

    /**
     * 立即执行任务
     */
    private void run(JobEntity jobEntity) {
        try {
            //参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(JOB_PARAM_KEY, toVo(jobEntity));

            scheduler.triggerJob(getJobKey(jobEntity.getId()), dataMap);

            log.info("run scheduler job success jobId=[{}]", jobEntity.getBeanName());

        } catch (SchedulerException e) {
            log.error("run scheduler job error, jobInfo=[{}]", jobEntity, e);
            throw ServiceException.of(ErrorCode.JOB_TRIGGER_ERROR, "run job error");
        }
    }

    /**
     * 暂停任务
     */
    private void pauseJob(JobEntity jobEntity) {
        try {
            scheduler.pauseJob(getJobKey(jobEntity.getId()));

            log.info("pause scheduler job success jobId=[{}]", jobEntity.getBeanName());

        } catch (SchedulerException e) {
            log.error("pause scheduler error jobId=[{}]", jobEntity.getBeanName(), e);
            throw ServiceException.of(ErrorCode.JOB_TRIGGER_ERROR, "pause job error");
        }
    }

    /**
     * 恢复任务
     */
    private void resumeJob(JobEntity jobEntity) {
        try {
            scheduler.resumeJob(getJobKey(jobEntity.getId()));

            log.info("resume scheduler job success jobId=[{}]", jobEntity.getBeanName());
        } catch (SchedulerException e) {
            log.error("resume scheduler error jobId=[{}]", jobEntity.getBeanName(), e);
            throw ServiceException.of(ErrorCode.JOB_TRIGGER_ERROR, "resume job error");
        }
    }

    /**
     * 删除定时任务
     */
    private void deleteScheduleJob(JobEntity jobEntity) {
        try {
            scheduler.deleteJob(getJobKey(jobEntity.getId()));

            log.info("delete scheduler job success jobId=[{}]", jobEntity.getBeanName());

        } catch (SchedulerException e) {
            log.error("delete scheduler error jobId=[{}]", jobEntity.getBeanName(), e);
            throw ServiceException.of(ErrorCode.JOB_TRIGGER_ERROR, "delete job error");
        }
    }


    /**
     * 获取触发器key
     */
    private TriggerKey getTriggerKey(Long jobId) {
        return TriggerKey.triggerKey(JOB_NAME + jobId);
    }

    /**
     * 获取jobKey
     */
    private JobKey getJobKey(Long jobId) {
        return JobKey.jobKey(JOB_NAME + jobId);
    }


    /**
     * 获取表达式触发器
     */
    private CronTrigger getCronTrigger(Long jobId) {
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            throw ServiceException.of(ErrorCode.JOB_TRIGGER_ERROR, "get cron trigger error");
        }
    }

    /**
     * 创建定时任务
     */
    private void createScheduleJob(JobEntity jobEntity) {
        try {
            //构建job信息
            JobDetail jobDetail =
                    JobBuilder.newJob(QuartzProxyJob.class).withIdentity(getJobKey(jobEntity.getId())).build();

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobEntity.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobEntity.getId()))
                    .withSchedule(scheduleBuilder).build();

            //放入参数，运行时的方法可以获取
            jobDetail.getJobDataMap().put(JOB_PARAM_KEY, toVo(jobEntity));

            scheduler.scheduleJob(jobDetail, trigger);

            //暂停任务
            if (jobEntity.getStatus().equals(StatusEnum.PAUSE.getValue())) {
                pauseJob(jobEntity);
            }

            log.info("create scheduler job success jobInfo=[{}]", jobEntity.getBeanName());

        } catch (SchedulerException e) {
            log.error("create scheduler job error job info=[{}]", jobEntity, e);
            throw ServiceException.of(ErrorCode.JOB_TRIGGER_ERROR, "create schedule job error");
        }
    }

    /**
     * 更新定时任务
     */
    private void updateScheduleJob(JobEntity jobEntity) {
        try {
            TriggerKey triggerKey = getTriggerKey(jobEntity.getId());

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobEntity.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = getCronTrigger(jobEntity.getId());

            //按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

            //参数
            trigger.getJobDataMap().put(JOB_PARAM_KEY, toVo(jobEntity));

            scheduler.rescheduleJob(triggerKey, trigger);

            //暂停任务
            if (Objects.equals(jobEntity.getStatus(), StatusEnum.PAUSE.getValue())) {
                pauseJob(jobEntity);
            }

            log.info("update scheduler job success jobInfo=[{}]", jobEntity.getBeanName());

        } catch (SchedulerException e) {
            log.error("update scheduler job error jobInfo=[{}]", jobEntity, e);
            throw ServiceException.of(ErrorCode.JOB_TRIGGER_ERROR, "update schedule job error");
        }
    }


    private JobEntityVO toVo(JobEntity jobEntity) {
        return modelMapper.map(jobEntity, JobEntityVO.class);
    }
}
